﻿using System;
using System.Collections.Generic;
using System.Reflection;
using Jayden.Dll.Core.Persistence.Attributes;
using Jayden.Dll.Core.Persistence.Tools;

namespace Jayden.Dll.Core.Persistence.Models
{
	public class Entity
	{
		private Namespace m_Namespace;
		private string m_Name;
		private Dictionary<string, Property> m_Properties;
		private Index m_PrimaryKey;
		private Table m_Table;
		private ObjectType m_ObjectType;
		private Type m_Type;

		public Entity(Namespace ns, string name, Table table, params Property[] propterties)
		{
			m_Name = name;
			m_Namespace = ns;
			m_Table = table;
			m_Properties = new Dictionary<string, Property>();
			foreach (Property p in propterties)
				m_Properties.Add(p.Name, p);
			m_ObjectType = new ObjectType(m_Table.ObjectType);
		}
		public Entity(Namespace ns, Type type)
		{
			if (type == null)
				throw new ArgumentNullException("type");
			m_Type = type;
			m_Namespace = ns;
			m_Name = type.Name;
			m_Table = null;
			object[] obj = type.GetCustomAttributes(typeof(Table), true);
			if (obj != null && obj.Length > 0)
			{
				for (int c = 0; m_Table == null && c < obj.Length; c++)
				{
					if (obj[c] is Table)
						m_Table = obj[c] as Table;
				}
			}
			if (m_Table == null)
				throw new InvalidOperationException();
			if (m_Table.ObjectType == null)
				m_Table.ObjectType = type.Name;
			if (m_Table.TableName == null)
				m_Table.TableName = type.Name;
			if (m_Table.EntityName == null)
				m_Table.EntityName = type.Name;
			if (m_Name == null)
				m_Name = type.Name;
			m_ObjectType = new ObjectType(m_Table.ObjectType);
			PropertyInfo[] properties = type.GetProperties();
			m_Properties = new Dictionary<string, Property>();
			for (int d = 0; d < properties.Length; d++)
			{
				PropertyInfo pi = properties[d];
				object[] fields = pi.GetCustomAttributes(typeof(Field), true);
				for (int c = 0; c < fields.Length; c++)
				{
					if (fields[c] is Field)
					{
						Field field = fields[c] as Field;
						if (field.FieldName == null)
							field.FieldName = pi.Name;
						if (field.PropertyName == null)
							field.PropertyName = pi.Name;
						if (field.Type == null)
						{
							field.Type = pi.PropertyType;
							field.DbType = SQLHelper.GetDefaultDbType(pi.PropertyType);
						}
						if (!field.Nullable && !field.Type.IsValueType)
							field.Nullable = true;
						if (!field.Nullable && field.Type.IsGenericType && field.Type.GetGenericTypeDefinition() == typeof(Nullable<>))
							field.Nullable = true;
						if (!field.Nullable && field.DefaultValue == null && field.Type.IsValueType)
							field.DefaultValue = Activator.CreateInstance(field.Type);
						Property property = new Property(this, pi.Name, field, pi);
						m_Properties.Add(pi.Name, property);
					}
				}
			}
		}

		public Index PrimaryKey
		{
			get
			{
				if (m_PrimaryKey == null)
				{
					List<Property> properties = new List<Property>();
					foreach (Property property in m_Properties.Values)
						if (property.Field.PrimaryKey)
							properties.Add(property);
					m_PrimaryKey = new Index(this, null, properties.ToArray());
				}
				return m_PrimaryKey;
			}
		}
		public Namespace Namespace { get { return m_Namespace; } }
		public string Name { get { return m_Name; } }
		public bool Contains(string propertyName) { return m_Properties.ContainsKey(propertyName); }
		public Property this[string propertyName] { get { return m_Properties[propertyName]; } }
		public Table Table { get { return m_Table; } }
		public ObjectType ObjectType { get { return m_ObjectType; } }
		public Type Type { get { return m_Type; } }

		public IEnumerable<Property> Properties { get { return m_Properties.Values; } }

		public string FullName { get { return string.Format("{0}.{1}", Namespace, Name); } }
		public override string ToString() { return FullName; }

	}
}
